#include "stdafx.h"
#include "EXScrollBarThumb.h"
#include "EXScrollBar.h"

// CEXScrollBarThumb

IMPLEMENT_DYNAMIC(CEXScrollBarThumb, CWnd)

CEXScrollBarThumb::CEXScrollBarThumb()
:LEAVER_TIMER(1)
,LEAVER_ELAPSE(10)
{
	m_ptPrev.x = 0;
	m_ptPrev.y = 0;
	m_bHorizonal = TRUE;
	m_nThumbBitmapID = 0;
	m_nThumbPics = 0;
	m_bSetThumbBitmap = FALSE;
	m_nLeftWidth = 0;
	m_nRightWidth = 0;
	m_nTopHeight = 0;
	m_nBottomHeight = 0;
	m_crTransparent = RGB(255,0,255);
	m_nBkgndBitmapID = 0;
	m_nIconBitmapID = 0;
	m_Status = SKIN_NORMAL;
	m_uLeaveTimerID = 0;
}

CEXScrollBarThumb::~CEXScrollBarThumb()
{
}


BEGIN_MESSAGE_MAP(CEXScrollBarThumb, CWnd)
	ON_WM_PAINT()
	ON_WM_NCHITTEST()
	ON_WM_MOUSEMOVE()
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONUP()
	ON_WM_SIZE()
	ON_WM_TIMER()
	ON_WM_ERASEBKGND()
END_MESSAGE_MAP()



// CEXScrollBarThumb message handlers



void CEXScrollBarThumb::OnPaint()
{
	CPaintDC dc(this);

	CRect rcClient;
	GetClientRect(rcClient);

	if (m_bSetThumbBitmap)
	{
		CDC dcBkgnd;
		dcBkgnd.CreateCompatibleDC(&dc);

		CBitmap bmpBkgnd;
		bmpBkgnd.CreateCompatibleBitmap(&dc, rcClient.Width(), rcClient.Height());

		CBitmap * pOldBitmap = NULL;
		pOldBitmap = dcBkgnd.SelectObject(&bmpBkgnd);

		CDC dcBitmap;
		dcBitmap.CreateCompatibleDC(&dc);

		BITMAP bmInfo = {0};
		m_bmpThumb.GetBitmap(&bmInfo);

		int nWidth = bmInfo.bmWidth / m_nThumbPics;

		CBitmap * pOldBitmapImage = NULL;
		pOldBitmap = dcBitmap.SelectObject(&m_bmpThumb);

		STATUS st;

		if (!IsWindowEnabled())
		{
			st = disable;
		}
		else
		{
			if (m_Status == SKIN_FOCUS)
				st = focus;
			else if (m_Status == SKIN_SELECT)
				st = select;
			else
				st = normal;
		}

		dcBkgnd.BitBlt(0, 0, rcClient.Width(), rcClient.Height(), 
      &dcBitmap, rcClient.Width()*st, 0, SRCCOPY);

		dcBitmap.SelectObject(pOldBitmap);

		dc.BitBlt(0,0,rcClient.Width(), rcClient.Height(), &dcBkgnd, 0, 0, SRCCOPY);
		dcBkgnd.SelectObject(pOldBitmap);
	}
	else
	{
		dc.FillSolidRect(&rcClient, GetSysColor(COLOR_SCROLLBAR));
		dc.Draw3dRect(&rcClient, RGB(192, 192, 192), RGB(0, 0, 0));
		rcClient.DeflateRect(1, 1);
		dc.Draw3dRect(&rcClient, RGB(255, 255, 255), RGB(128, 128, 128));
	}
}

LRESULT CEXScrollBarThumb::OnNcHitTest(CPoint point)
{
	return CWnd::OnNcHitTest(point);
}

void CEXScrollBarThumb::OnMouseMove(UINT nFlags, CPoint point)
{
	if (nFlags & MK_LBUTTON)
	{
		CEXScrollBar * pScroll = ((CEXScrollBar*)GetParent());
		if (m_bHorizonal)
		{
			if (point.x != m_ptPrev.x)
			{
				int nInterVal = point.x - m_ptPrev.x;

				CRect rcWnd;
				GetWindowRect(rcWnd);
				GetParent()->ScreenToClient(rcWnd);

				rcWnd.OffsetRect(nInterVal, 0);

				CRect rcScroll = pScroll->m_rcScroll;

				int nWidth = rcWnd.Width();

				if (rcScroll.left > rcWnd.left)
				{
					rcWnd.left = rcScroll.left;
					rcWnd.right = rcWnd.left + nWidth;
				}
				else if (rcScroll.right < rcWnd.right)
				{
					rcWnd.right = rcScroll.right;
					rcWnd.left = rcWnd.right - nWidth;
				}
        
				BOOL bContinued = FALSE;
				if (nInterVal>0 && (m_nPrevPos >= (pScroll->m_nMax - pScroll->m_nPage)))
					bContinued = TRUE;
				
				pScroll->SetThumbPosition(rcWnd, bContinued);
				m_nPrevPos = pScroll->m_nPos;
			}
		}
		else
		{
			if (point.y != m_ptPrev.y)
			{
				int nInterVal = point.y - m_ptPrev.y;

				CRect rcWnd;
				GetWindowRect(rcWnd);
				GetParent()->ScreenToClient(rcWnd);

				rcWnd.OffsetRect(0, nInterVal);

				CRect rcScroll = pScroll->m_rcScroll;

				int nHeight = rcWnd.Height();

				if (rcScroll.top > rcWnd.top)
				{
					rcWnd.top = rcScroll.top;
					rcWnd.bottom = rcWnd.top + nHeight;
				}
				else if (rcScroll.bottom < rcWnd.bottom)
				{
					rcWnd.bottom = rcScroll.bottom;
					rcWnd.top = rcWnd.bottom - nHeight;
				}
        
				BOOL bContinued = FALSE;
				if (nInterVal>0 && (m_nPrevPos >= (pScroll->m_nMax - pScroll->m_nPage)))
					bContinued = TRUE;

				pScroll->SetThumbPosition(rcWnd, bContinued);
				m_nPrevPos = pScroll->m_nPos;
			}
		}

		m_Status = SKIN_SELECT;
		RedrawWindow(NULL, NULL, RDW_UPDATENOW|RDW_NOERASE|RDW_INVALIDATE);
	}
	else
	{
		if (!m_uLeaveTimerID)
			m_uLeaveTimerID = SetTimer(LEAVER_TIMER, LEAVER_TIMER, NULL);

		m_Status = SKIN_FOCUS;
		RedrawWindow(NULL, NULL, RDW_UPDATENOW|RDW_NOERASE|RDW_INVALIDATE);
	}

	CWnd::OnMouseMove(nFlags, point);
}

void CEXScrollBarThumb::OnLButtonDown(UINT nFlags, CPoint point)
{
	CEXScrollBar * pScroll = ((CEXScrollBar*)GetParent());
	pScroll->SetListErase(FALSE);

	m_nPrevPos = pScroll->m_nPos;
	m_ptPrev = point;
	SetCapture();


	if (m_uLeaveTimerID)
	{
		KillTimer(LEAVER_TIMER);
		m_uLeaveTimerID = 0;
	}

	m_Status = SKIN_SELECT;
	RedrawWindow(NULL, NULL, RDW_UPDATENOW|RDW_NOERASE|RDW_INVALIDATE);

	CWnd::OnLButtonDown(nFlags, point);
}

void CEXScrollBarThumb::OnLButtonUp(UINT nFlags, CPoint point)
{
	CEXScrollBar * pScroll = ((CEXScrollBar*)GetParent());
	pScroll->SetListErase(TRUE);

	m_nPrevPos = 0;
	m_ptPrev.x = 0;
	m_ptPrev.y = 0;
	ReleaseCapture();

	CRect rcClient;
	GetClientRect(rcClient);

	if (rcClient.PtInRect(point))
		m_Status = SKIN_FOCUS;
	else
		m_Status = SKIN_NORMAL;

	RedrawWindow(NULL, NULL, RDW_UPDATENOW|RDW_NOERASE|RDW_INVALIDATE);

	CWnd::OnLButtonUp(nFlags, point);
}

BOOL CEXScrollBarThumb::Create(const RECT& rect, CWnd* pParentWnd, UINT nID, 
                               BOOL bHorizontal)
{
	m_bHorizonal = bHorizontal;

	return CWnd::Create(NULL, NULL, WS_CHILD|WS_VISIBLE, rect, pParentWnd, 
                      nID, NULL);
}

void CEXScrollBarThumb::OnSize(UINT nType, int cx, int cy)
{
	CWnd::OnSize(nType, cx, cy);

	if (IsWindow(GetSafeHwnd()))
	{
		if (m_bSetThumbBitmap)
			SetThumbBitmap(m_nThumbBitmapID, m_nBkgndBitmapID, m_nIconBitmapID, 
      m_nThumbPics, m_nLeftWidth, m_nRightWidth, m_nTopHeight, m_nBottomHeight);

		RedrawWindow(NULL, NULL, RDW_UPDATENOW|RDW_NOERASE|RDW_INVALIDATE);
	}
}

BOOL CEXScrollBarThumb::SetThumbBitmap(UINT nThumbID, UINT nBkgndID, UINT nIconID, 
                                       int nPics, int nLeftWidth /* = 0*/, 
                                       int nRightWidth /* = 0*/,
									                      int nTopHeight /* = 0*/, 
                                        int nBottomHeight /* = 0*/)
{
	m_nThumbBitmapID = nThumbID;
	m_nBkgndBitmapID = nBkgndID;
	m_nIconBitmapID = nIconID;
	m_nThumbPics = nPics;
	m_bSetThumbBitmap = TRUE;

	m_nLeftWidth = nLeftWidth;
	m_nRightWidth = nRightWidth;
	m_nTopHeight = nTopHeight;
	m_nBottomHeight = nBottomHeight;

	CBitmap bmp;
	if( !bmp.LoadBitmap(nThumbID) )
		return FALSE;

	m_bmpThumb.DeleteObject();

	if( !InitBgBitmap( bmp, m_bmpThumb, normal, nPics, FALSE ) )
		return FALSE;
	if( !InitBgBitmap( bmp, m_bmpThumb, select, nPics, FALSE ) )
		return FALSE;
	if( !InitBgBitmap( bmp, m_bmpThumb, disable, nPics, FALSE ) )
		return FALSE;
	if( !InitBgBitmap( bmp, m_bmpThumb, focus, nPics, FALSE ) )
		return FALSE;

	bmp.DeleteObject();

	return TRUE;
}

BOOL CEXScrollBarThumb::InitBgBitmap( CBitmap & bmpSrc, CBitmap & bmpDest,
                                     int nIndex, int nCount, BOOL bReSize )
{

	CDC * pDC = GetDC();

	CDC memDC; 
	memDC.CreateCompatibleDC(pDC);

	CDC srcDC;
	srcDC.CreateCompatibleDC(pDC);

	CBitmap* pOldBitmap1;
	pOldBitmap1 = srcDC.SelectObject(&bmpSrc);

	BITMAP bmpinfo;
	bmpSrc.GetBitmap(&bmpinfo);
	int bmpWidth = bmpinfo.bmWidth / nCount;
	int bmpHeight = bmpinfo.bmHeight;
	int orix = bmpWidth * (nIndex < nCount ? nIndex : 0);

	CRect rc;
	GetClientRect(rc);

	if (!bmpDest.GetSafeHandle())
		bmpDest.CreateCompatibleBitmap(pDC,rc.Width()*nCount,rc.Height());

	CBitmap bmpTemp;
	bmpTemp.CreateCompatibleBitmap(pDC,rc.Width(),rc.Height());

	CBitmap* pOldBitmap2;
	pOldBitmap2 = memDC.SelectObject(&bmpTemp);

	if (m_nBkgndBitmapID)
	{
		CBitmap bmpBkgnd;
		bmpBkgnd.LoadBitmap(m_nBkgndBitmapID);

		BITMAP bmBkgnd;
		bmpBkgnd.GetBitmap(&bmBkgnd);

		int bmpBkgndWidth = bmBkgnd.bmWidth / nCount;
		int bmpBkgndHeight = bmBkgnd.bmHeight;
		int orixBkgnd = bmpBkgndWidth * (nIndex < nCount ? nIndex : 0);

		CDC dcBkgnd;
		dcBkgnd.CreateCompatibleDC(pDC);

		CBitmap * pOldBkgnd = dcBkgnd.SelectObject(&bmpBkgnd);

		memDC.StretchBlt(0, 0, rc.Width(), rc.Height(), &dcBkgnd, orixBkgnd, 0, 
      bmpBkgndWidth, bmpBkgndHeight, SRCCOPY);

		dcBkgnd.SelectObject(pOldBkgnd);
	}

	if (!m_nLeftWidth && !m_nRightWidth && !m_nTopHeight && !m_nBottomHeight)
	{
		memDC.TransparentBlt(0, 0, rc.Width(), rc.Height(), &srcDC, orix, 0, 
      bmpWidth, bmpHeight, m_crTransparent);
	}
	else
	{
		// entireimage
		memDC.TransparentBlt(m_nLeftWidth,
			m_nTopHeight,
			rc.Width()  - ( m_nLeftWidth + m_nRightWidth ) ,
			rc.Height() -  ( m_nTopHeight + m_nBottomHeight) ,
			&srcDC,
			orix + m_nLeftWidth,
			m_nTopHeight,
			bmpWidth - ( m_nLeftWidth + m_nRightWidth ) ,
			bmpHeight - ( m_nTopHeight + m_nBottomHeight ) , m_crTransparent);

		// topbar,bottombar( Stretch )
		memDC.TransparentBlt(m_nLeftWidth,0, rc.Width() - 
      (m_nLeftWidth + m_nRightWidth), m_nTopHeight, &srcDC,orix + m_nLeftWidth, 
      0, bmpWidth - ( m_nLeftWidth + m_nRightWidth),
      m_nTopHeight, m_crTransparent);

		memDC.TransparentBlt(m_nLeftWidth, rc.bottom - m_nBottomHeight, 
      rc.Width() - ( m_nLeftWidth + m_nRightWidth), m_nBottomHeight,
			&srcDC,orix + m_nLeftWidth,bmpHeight - m_nBottomHeight,bmpWidth - 
      ( m_nLeftWidth + m_nRightWidth) , m_nBottomHeight, m_crTransparent );

		// sidebar ( STretch? )
		memDC.TransparentBlt(0,m_nTopHeight,m_nLeftWidth,rc.bottom - m_nTopHeight - 
      m_nBottomHeight ,	&srcDC, orix,m_nTopHeight, m_nLeftWidth, bmpHeight - 
      ( m_nTopHeight + m_nBottomHeight ) , m_crTransparent);

		memDC.TransparentBlt(rc.right - m_nRightWidth ,m_nTopHeight,m_nRightWidth,
      rc.bottom - m_nTopHeight - m_nBottomHeight, &srcDC, orix +  bmpWidth - 
      m_nRightWidth,m_nTopHeight, m_nRightWidth, bmpHeight - m_nTopHeight - 
      m_nBottomHeight , m_crTransparent);

		// lefttop,leftbottom,righttop,rightbottom
		if( !memDC.TransparentBlt(0,0,m_nLeftWidth, m_nTopHeight, &srcDC, orix,0, 
      m_nLeftWidth, m_nTopHeight, m_crTransparent) ) return FALSE;

		if( !memDC.TransparentBlt(0,rc.bottom - m_nBottomHeight,m_nLeftWidth,
      m_nBottomHeight, &srcDC,orix,bmpHeight - m_nBottomHeight,m_nLeftWidth, 
      m_nBottomHeight, m_crTransparent) ) return FALSE;

		if( !memDC.TransparentBlt(rc.right - m_nRightWidth, 0 ,m_nRightWidth, 
      m_nTopHeight, &srcDC,orix + bmpWidth - m_nRightWidth,0,m_nRightWidth, 
      m_nTopHeight, m_crTransparent) ) return FALSE;

		if( !memDC.TransparentBlt(rc.right - m_nRightWidth,rc.bottom - 
      m_nBottomHeight,m_nRightWidth,m_nBottomHeight, &srcDC,orix + bmpWidth - 
      m_nRightWidth,bmpHeight - m_nBottomHeight,m_nRightWidth, m_nBottomHeight, 
      m_crTransparent) ) return FALSE;
	}

	if (m_nIconBitmapID)
	{
		CBitmap bmpIcon;
		bmpIcon.LoadBitmap(m_nIconBitmapID);

		BITMAP bmIcon;
		bmpIcon.GetBitmap(&bmIcon);

		int bmpIconWidth = bmIcon.bmWidth / nCount;
		int bmpIconHeight = bmIcon.bmHeight;
		int orixIcon = bmpIconWidth * (nIndex < nCount ? nIndex : 0);

		CDC dcBkgnd;
		dcBkgnd.CreateCompatibleDC(pDC);

		CBitmap * pOldIcon = dcBkgnd.SelectObject(&bmpIcon);

		int nSrcX = (rc.Width() - bmpIconWidth) / 2;
		int nSrcY = (rc.Height() - bmpIconHeight) / 2;

		memDC.TransparentBlt(nSrcX, nSrcY, bmpIconWidth, bmpIconHeight, &dcBkgnd, 
      orixIcon, 0, bmpIconWidth, bmpIconHeight, m_crTransparent);

		dcBkgnd.SelectObject(pOldIcon);
	}

	CDC dcThumb;
	dcThumb.CreateCompatibleDC(pDC);

	CBitmap * pOldBitmap3;
	pOldBitmap3 = dcThumb.SelectObject(&bmpDest);

	dcThumb.BitBlt(rc.Width()*nIndex, 0, rc.Width(), rc.Height(), 
    &memDC, 0, 0, SRCCOPY);

	srcDC.SelectObject(pOldBitmap1);
	memDC.SelectObject(pOldBitmap2);
	dcThumb.SelectObject(pOldBitmap3);

	ReleaseDC(pDC);
	ReleaseDC(&srcDC);
	ReleaseDC(&memDC);
	ReleaseDC(&dcThumb);

	return TRUE;
}

void CEXScrollBarThumb::OnTimer(UINT_PTR nIDEvent)
{
	if (nIDEvent == m_uLeaveTimerID)
	{
		POINT ptCur;
		GetCursorPos(&ptCur);
		ScreenToClient(&ptCur);

		CRect rcClient;
		GetClientRect(rcClient);

		if (!rcClient.PtInRect(ptCur))
		{
			KillTimer(LEAVER_TIMER);
			m_uLeaveTimerID = 0;

			m_Status = SKIN_NORMAL;
			RedrawWindow(NULL, NULL, RDW_UPDATENOW|RDW_NOERASE|RDW_INVALIDATE);
		}
	}

	CWnd::OnTimer(nIDEvent);
}

BOOL CEXScrollBarThumb::OnEraseBkgnd(CDC* pDC)
{
	UNREFERENCED_PARAMETER(pDC);
	return FALSE;
}
